﻿

using System;
using Microsoft.Xna.Framework;
namespace GameEngine
{
    /// <summary>
    /// Contains all the methods that involve physics in any way.
    /// </summary>
    public static class PhysicsEngine
    {

        public static float Gravity = 98f;
        public static float GravityFalloff = 200f;


        #region Detect Collisions

        public static bool IsColliding(PhysicsComponent A, PhysicsComponent B)
        {
            //Since all we have is just basic physics components at the time of programming this
            //Im leaving it like this, when i add more types of 2d collisions like Circle collision
            //Then i will add the highest inherited type first, and the lowest last (Physics Component)

            if (A.GetType() == typeof(PhysicsComponent))
            {
                if (B.GetType() == typeof(PhysicsComponent))
                {
                    if (PhysRectangle.Intersecting(A.Rectangle, B.Rectangle))
                    {
                        return true;
                    }
                    else { return false; }
                }
            }

            return false;
        }

        #endregion

        #region Physics Drivers

        public static void ApplyGravity(PhysicsComponent component, GameTime gameTime)
        {

            VelocityComponent VComp = component.Object.Get<VelocityComponent>();

            if (VComp.Velocity == 0)
            {
                VComp.Angle = MathHelper.Pi;
                VComp.Velocity -= Gravity * (float)gameTime.ElapsedGameTime.TotalSeconds;
                return;
            }

            if (VComp.Angle == 0)
            {
                if (VComp.Velocity + Gravity < GravityFalloff)
                {

                    if (VComp.Velocity + Gravity > GravityFalloff)
                    {
                        VComp.Velocity = GravityFalloff;
                    }
                    else
                    {
                        VComp.Velocity += Gravity * (float)gameTime.ElapsedGameTime.TotalSeconds;
                    }

                }

                return;
            }

            if (VComp.Angle == MathHelper.Pi)
            {
                VComp.Velocity -= Gravity * (float)gameTime.ElapsedGameTime.TotalSeconds;
                if (VComp.Velocity < 0) { VComp.Angle = 0; }
                return;
            }

            VComp.Angle = PhysicsEngine.ApproachAngle(0, VComp.Angle, (Gravity / VComp.Velocity) * (float)gameTime.ElapsedGameTime.TotalSeconds);

        }

        #region Helpers
        /// <summary>
        /// Used to simulate stuff, look for code samples in Physics Engine
        /// Strictly is for Radians / Angles
        /// In theory, the Current will never be able to approach the Target
        /// Current + (Difference*Percent) = NewCurrent
        /// </summary>
        /// <param name="Target">Target Value</param>
        /// <param name="Current">Curent Value</param>
        /// <param name="Percent">The Amount the increase is</param>
        public static float ApproachAngle(float Target, float Current, float Amount)
        {

            if (Target > 0)
            {
                if (Current > 0)
                {
                    if (Current > Target)
                    {
                        if (Current - Amount > Target)
                        {
                            return Current - Amount;
                        }
                        return Target;
                    }
                }

                if ((Math.PI + Current) + (Math.PI - Target) < (Target - Current))
                {
                    return Current - Amount;
                }

                if (Current + Amount > Target)
                {
                    return Target;
                }
                return Current + Amount;

            }
            if (Current > 0)
            {
                if ((Math.PI - Current) + (Math.PI + Target) < (Current - Target))
                {
                    return Current + Amount;
                }

                if (Current - Amount < Target)
                {
                    return Target;
                }
                return Current - Amount;
            }
            if (Current - Amount < Target)
            {
                return Target;
            }
            return Current - Amount;

        }

        #endregion


        #endregion

        #region Velocity Driver

        public static void MoveObject(VelocityComponent component, GameTime gameTime)
        {
            if (component.Velocity < 0)
            {
                component.Velocity = -component.Velocity;
                component.Angle = WrapAngle(-component.Angle);
            }

            GameObject gameObject = component.Object;
            PositionalComponent position = gameObject.Get<PositionalComponent>();

            float X = (float)Math.Sin((double)component.Angle);
            float Y = (float)Math.Cos((double)component.Angle);
            Vector2 Vec = new Vector2(X, Y);

            position.Position = position.Position + (Vec * (component.Velocity * (float)gameTime.ElapsedGameTime.TotalSeconds));

            component.Angle = WrapAngle(component.Angle);

        }



        #region HelperFuncs
        /// <summary>
        /// Returns the angle expressed in radians between -Pi and Pi.
        /// <param name="radians">the angle to wrap, in radians.</param>
        /// <returns>the input value expressed in radians from -Pi to Pi.</returns>
        /// </summary>
        public static float WrapAngle(float radians)
        {
            while (radians < -MathHelper.Pi)
            {
                radians += MathHelper.TwoPi;
            }
            while (radians > MathHelper.Pi)
            {
                radians -= MathHelper.TwoPi;
            }
            return radians;
        }
        #endregion

        #endregion


    }


}